/**********************************************************************************************/
/* FBL_Exceptions.cpp                                                      					  */
/*                                                                       					  */
/* Copyright Paradigma, 1993-2006                                        					  */
/* All Rights Reserved.                                                 					  */
/**********************************************************************************************/

#include <VShared/FBL/publ/Headers/StdAfx.h>

#include <VShared/FBL/publ/Errors/FBL_Exceptions.h>

#include <VShared/FBL/publ/Util_classes/FBL_String.h>
#include <VShared/FBL/publ/Util_classes/FBL_String.h>
#include <VShared/FBL/publ/Util_Strings/FBL_ustdio.h>

#include <VShared/FBL/publ/Util_classes/FBL_StringConversion.h>

#include <VShared/FBL/publ/Interfaces/FBL_I_Connection.h>


/**********************************************************************************************/
FBL_Begin_Namespace


/**********************************************************************************************/
ErrorEntry* FindErrorEntry( ERROR_TYPE inErrorType )
{
	ErrorEntry* pLeft 	= gErrorMap;
	ErrorEntry* pRight	= gErrorMap + (gErrorMapSize - 1);
	ErrorEntry* pMiddle;
	
	while( pLeft < pRight )
	{
		pMiddle = pLeft + ((pRight - pLeft) >> 1);
		if( pMiddle->mCode < inErrorType )
				pLeft 	= pMiddle + 1;
		else	pRight	= pMiddle;
	}

	return ( pRight->mCode == inErrorType ) ? pRight : NULL;
}


/**********************************************************************************************/
ErrorEntry* FindErrorEntry( const char*	inErrorStr )
{
	FBL_CHECK( inErrorStr );

	ErrorEntry* result(NULL);

	for( ulong i = 0; i < gErrorMapSize; ++i )
	{
#if FBL_WIN
		if( strcmpi( gErrorMap[i].mSymbol, inErrorStr ) == 0 )
#else
		if( strcasecmp( gErrorMap[i].mSymbol, inErrorStr ) == 0 )
#endif
		{
			result = &gErrorMap[i];
			break;
		}
	}

	return result;
}


/**********************************************************************************************/
xException::xException( 
	ERROR_TYPE inErrorCode, 
	const UChar* inArg1,
	const UChar* inArg2,
	const UChar* inArg3 )
:
	mErrorCode( inErrorCode ),
	mpConnection(0)
{	
	mArgs[0] = inArg1 ? new String(inArg1) : NULL;
	mArgs[1] = inArg2 ? new String(inArg2) : NULL;
	mArgs[2] = inArg3 ? new String(inArg3) : NULL;
}


/**********************************************************************************************/
xException::xException( 
	ERROR_TYPE	inErrorCode,
	const char* inArg1,
	const char* inArg2,
	const char* inArg3 )
:
	mErrorCode( inErrorCode ),
	mpConnection(0)
{
	mArgs[0] = inArg1 ? new String(inArg1) : NULL;
	mArgs[1] = inArg2 ? new String(inArg2) : NULL;
	mArgs[2] = inArg3 ? new String(inArg3) : NULL;
}


/**********************************************************************************************/
xException::xException( 
	ERROR_TYPE inErrorCode,
	const String& inFinalMessage,
	I_Connection* inConn )
:
	mErrorCode( inErrorCode ),
	mpConnection(inConn)
{
	if( mpConnection )
		mpConnection->AddRef();

	mArgs[0] = inFinalMessage.isEmpty() ? NULL : new String(inFinalMessage);
	mArgs[1] = NULL;
	mArgs[2] = NULL;
}


/**********************************************************************************************/
xException::xException( const xException& inExc )
{
	mpConnection = inExc.mpConnection;
	if( mpConnection )
		mpConnection->AddRef();

	mErrorCode = inExc.mErrorCode;
	mArgs[0] = inExc.mArgs[0] ? new String(*inExc.mArgs[0]) : NULL;
	mArgs[1] = inExc.mArgs[1] ? new String(*inExc.mArgs[1]) : NULL;
	mArgs[2] = inExc.mArgs[2] ? new String(*inExc.mArgs[2]) : NULL;
}


/**********************************************************************************************/
xException& xException::operator =( const xException& inExc )
{
	Clear();

	mErrorCode = inExc.mErrorCode;
	mArgs[0] = inExc.mArgs[0] ? new String(*inExc.mArgs[0]) : NULL;
	mArgs[1] = inExc.mArgs[1] ? new String(*inExc.mArgs[1]) : NULL;
	mArgs[2] = inExc.mArgs[2] ? new String(*inExc.mArgs[2]) : NULL;
	
	return *this;
}


/**********************************************************************************************/
xException::~xException( void )
{
	Clear();
}


/**********************************************************************************************/
void xException::Clear( void )
{
	ForgetObject( mArgs[0] );
	ForgetObject( mArgs[1] );
	ForgetObject( mArgs[2] );

	if( mpConnection )
		mpConnection->Release();
}


#pragma mark -


/**********************************************************************************************/
const String& xException::get_ErrorString( void ) const
{
static String s;
static String unknown("UNKNOWN");	

 	if( !mpConnection )
	{
		ErrorEntry* pE = FBL::FindErrorEntry( mErrorCode );

		const UChar* args[] = {
			mArgs[0] ? mArgs[0]->c_str() : unknown.c_str(),
			mArgs[1] ? mArgs[1]->c_str() : unknown.c_str(),
			mArgs[2] ? mArgs[2]->c_str() : unknown.c_str()
		};

		s = FormatErrorMessage( pE, args );
	}
	else
	{
		// Check whether we have the last error string desription.
		// It may be sent by the server on fatal connection error.
		if( mArgs[0] )
			return *mArgs[0];

		// Request the VServer for the last error description.
		s = mpConnection->get_ErrorDescription();
	}

	return s;
}


/**********************************************************************************************/
const String& xException::get_Param( ulong inIndex ) const throw()
{
static String s;

	FBL_CHECK(inIndex <= 3);
	switch (inIndex)
	{
		case 1:
		case 2:
		case 3:
			return mArgs[inIndex - 1] ? *mArgs[inIndex - 1] : s;
			
		default:
			return s;
	};
}


#pragma mark -


/**********************************************************************************************/
// This function search for %S
//
UChar* FormatErrorMessage_LocateS( 
	const UChar* inFormatString )
{
	UChar* pProcentChar = (UChar*) inFormatString;
	
	do
	{
		pProcentChar = vu_strchr( pProcentChar, UChar('%') );
		
		if( pProcentChar == NULL )
			return NULL;
		else
		{
			if( *(pProcentChar+1) == UChar('S') )
				return pProcentChar;
			else
				++pProcentChar;
		}

	}while( true );
	
	return NULL;	
}


/**********************************************************************************************/
// This function implement simplest behavior of printf() for UChar*
//
void FormatErrorMessage_printf( 
		UChar*	ioBuffer,
		ulong	inBufferSize, 
		const UChar* inFormatString,
		const UChar* inStr1 = NULL,
		const UChar* inStr2 = NULL,
		const UChar* inStr3 = NULL )
{
	// to be able use loop, we pack parameter into array.
	const UChar* ptrs[3] = { inStr1, inStr2, inStr3 };
	ulong items = 0;
	
	if( inStr3 ) 
		items = 3;
	else if( inStr2 ) 
		items = 2;
	else if( inStr1 ) 
		items = 1;
	
	UChar* pd = ioBuffer;
	UChar* ps = (UChar*) inFormatString;
	
	ulong i = 0;
	
	do{
		// we find next %U in the format string:
		// pE point on found % or after end of format string.
		UChar* pPS = FormatErrorMessage_LocateS( ps );
		if( pPS )
		{
			tslen ToCopy = tslen(pPS - ps);
			ToCopy = FBL::Min( ToCopy, (tslen)inBufferSize  );

			vu_strncpy( pd, ps, ToCopy );
			pd += ToCopy;
			ps += ToCopy;			
			inBufferSize -= ToCopy;
			if( inBufferSize == 0 )
				break;
			
			if( *pPS == UChar('%') )
			{
				const UChar* pstr = ptrs[i++];				
				if( pstr )
				{
					ulong plen = (ulong) vu_strlen( pstr );
					plen = FBL::Min(plen, inBufferSize);

					vu_strncpy( pd, pstr, (long) plen );
					pd += plen;
					inBufferSize -= plen; 
				}
				else // just copy %S from format string.
				{
					vu_strncpy( pd, pPS, 2 );
					pd += 2;
					inBufferSize -= 2;
				}
				
				ps += 2;
			}
			else
			{
				// we have not found %U, so break loop.
				break;
			}
		}
		else // we have come to the end of format string.
		{
			vu_strncpy( pd, ps, (long) inBufferSize );
			break;
		}
		
	}while( inBufferSize );	
}


/**********************************************************************************************/
const UChar* FormatErrorMessage ( 
	ErrorEntry*  inErrorEntry, 
	const UChar* const inArgs[] )
{
static UChar buffer[1024];

	if( !inErrorEntry || inErrorEntry->mDescription == NULL )
		return NULL;

	if( inArgs[2] != NULL )
		FormatErrorMessage_printf( buffer, 1024, inErrorEntry->mDescription, inArgs[0], inArgs[1], inArgs[2] );
	else if( inArgs[1] != NULL )
		FormatErrorMessage_printf( buffer, 1024, inErrorEntry->mDescription, inArgs[0], inArgs[1] );
	else if( inArgs[0] != NULL )
		FormatErrorMessage_printf( buffer, 1024, inErrorEntry->mDescription, inArgs[0] );
	else
		return inErrorEntry->mDescription; 

	return buffer;
}


/**********************************************************************************************/
// RZ: This method is used only by V4MD now. Hmm.
//
const char* FormatErrorMessage( 
	ErrorEntry*  inErrorEntry, 
	const char* const inArgs[] )
{

static StFromUTF16 st((UChar*)NULL);

static UChar arg1[256];
static UChar arg2[256];
static UChar arg3[256];

	if( inArgs[0] )
		vu_strcpy(arg1, inArgs[0]);
	else
		arg1[0] = 0;

	if( inArgs[1] )
		vu_strcpy(arg2, inArgs[1]);
	else
		arg2[0] = 0;

	if( inArgs[2] )
		vu_strcpy(arg3, inArgs[2]);
	else
		arg3[0] = 0;

	const UChar* args[] = {
		arg1,
		arg2,
		arg3
	};

	const UChar* pStr = FormatErrorMessage(inErrorEntry, args);
	st.Convert(pStr);
	
	return st.c_str();

}


/**********************************************************************************************/
FBL_End_Namespace
